#include "SocketIO.h"
#include <iostream>
#include <sys/types.h>

using std::cout;
using std::endl;
using std::cerr;

#include <unistd.h>
#include <sys/socket.h>

SocketIO::SocketIO(int fd)
: _fd(fd)
{
}


size_t SocketIO::readn(char *buf, size_t len)
{
    size_t left = len;
    char *pstr = buf;
    while(left > 0)
    {
        int ret = read(_fd, pstr, left);
        if(ret == -1 && errno == EINTR)
        {
            cerr << "read" << endl;
            continue;
        }
        else if(ret == -1)
        {
            cerr << "read" << endl;
            return -1;
        }
        else if(ret == 0)
        {
            break;
        }
        else
        {
            pstr += ret;
            left -= ret;
        }
    }
    return len - left;
}

size_t SocketIO::readLine(char* buf, size_t len)
{
    size_t left = len - 1;
    int ret = 0, total = 0;
    char* pstr = buf;

    while(left > 0)
    {
        ret = ::recv(_fd, pstr, left, MSG_PEEK);
        if(ret == -1 && errno == EINTR)
        {
            continue;
        }
        else if(ret == -1)
        {
            perror("recv");
            return -1;
        }
        else if(ret == 0)
        {
            break;
        }
        else
        {
            for(int i = 0; i < ret; ++i)
            {
                if(pstr[i] == '\n')
                {
                    int sz = i + 1;
                    readn(pstr, sz);
                    pstr += sz;
                    *pstr =  '\0';
                    return total + sz;
                }
            }
        }

        readn(pstr, ret);
        pstr += ret;
        left -= ret;
        total += ret;
    }
    *pstr = '\0';
    return len - left;
}


size_t SocketIO::writen(const char *buf, size_t len)
{
    size_t left = len;
    const char* pstr = buf;
    int ret = 0;
    while(left > 0)
    {
        ret = ::write(_fd, pstr, left);
        if(ret == -1 && errno == EINTR)
        {
            continue;
        }
        else if(ret == -1)
        {
            perror("write");
            return -1;
        }
        else if(ret == 0)
        {
            break;
        }
        else
        {
            pstr += ret;
            left -= ret;
        }
    }
    return len - left;
}

ssize_t SocketIO::writeFromBuffer(BufferType type)
{
    Buffer &_buffer = getBuffer(type);
    if(_buffer.readableBytes() == 0)
    {
        return 0;
    }

    size_t bytesToSend = _buffer.readableBytes();
        /* std::cout << "[Buffer] Before writeFromBuffer - readable: " << bytesToSend */
        /*       << ", content: " << std::string(_buffer.peek(), std::min(bytesToSend, size_t(50))) << std::endl; */
    ssize_t n = ::write(_fd, _buffer.peek(), _buffer.readableBytes());
    if(n > 0)
    {
        _buffer.retrieve(n);
    }
    return n;
}

ssize_t SocketIO::readToBuffer(BufferType type)
{
    return getBuffer(type).readFd(_fd);
}


ssize_t SocketIO::flushBuffer(BufferType type) {
    Buffer& buffer = getBuffer(type);
    const char* bufferName = (type == INPUT) ? "INPUT" : "OUTPUT";
    size_t bytesToSend = buffer.readableBytes();

    if (bytesToSend == 0) {
        return 0;
    }

    /* cout << "[SocketIO] flushBuffer - sending " << bytesToSend */
    /*      << " bytes from " << bufferName << " buffer" << endl; */

    ssize_t n = ::write(_fd, buffer.peek(), bytesToSend);

    if (n > 0) {
        buffer.retrieve(n);
        /* cout << "[SocketIO] flushBuffer - sent " << n << " bytes from " */
        /*      << bufferName << " buffer, " << buffer.readableBytes() */
        /*      << " bytes remaining" << endl; */
    } 

    return n;
}
